﻿using Borche.PlasticOPC;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Timers;

namespace BorchePlastic
{
    //=============================================================================
    //Copyright (C) 广州中和互联网技术有限公司 
    //description ：盟立机器类
    //author：杨金波 
    //time：2018-07-31 12:13:22      
    //=============================================================================

    /// <summary>
    /// 盟立机器类
    /// </summary>
    public class MirleMachine:IPlasticMachine
    {
        #region 私有变量
        private Socket _tcpClient;
        private string _MoldOpendParam;
        private IList<string> _QualityParams;
        private bool _Stop=false;

        /// <summary>
        /// 定时器
        /// </summary>
        protected Timer MyTimer { get; private set; }
        private bool _online = false;
        /// <summary>
        /// 产品计数器
        /// </summary>
        private int _ProductCounter;
        //当前模运行数据
        private IList<RunData> _RunDataList;
        /// <summary>
        /// 参数索引
        /// </summary>
        private int _ItemIndex;
        #endregion
        /// <summary>
        /// 订阅机器参数，完成数据订阅
        /// </summary>
        public Dictionary<int, string> MachineParams { get; private set; } = new Dictionary<int, string>();
        /// <summary>
        /// 订阅
        /// </summary>
        /// <returns></returns>
        public bool SubscribParams()
        {
            return true;
        }
        private bool _MoldCloseing = false;
        private bool _MoldOpening = false;
        private int _getFrequent = 10000;
        private DateTime _nextTime;
        /// <summary>
        /// 数据读取时间
        /// </summary>
        private DateTime _dataReadDateTime;
        private int OperationMode;
        #region 属性
        /// <summary>
        /// 通讯类型 
        /// </summary>
        public CommType commType { get; private set; } = CommType.TCP;
        /// <summary>
        /// 通讯类型名称
        /// </summary>
        public string commTypeName
        {
            get
            {
                return commType.ToString();
            }
        }
        /// <summary>
        /// 控制器类型
        /// </summary>
        public ControllerType CtrType
        {
            get; set;
        }
        /// <summary>
        /// 控制器类型名称
        /// </summary>
        public string CtrTypeName
        {
            get { return CtrType.ToString(); }
        }

        /// <summary>
        /// 运行数据列表
        /// </summary>
        public IList<RunData> RunDataList
        {
            get
            {
                if (_RunDataList == null)
                {
                    _RunDataList = new List<RunData>();
                }
                return _RunDataList;
            }
        }

        private Dictionary<string, string> _LastKeyValues = new Dictionary<string, string>();
        /// <summary>
        /// 参数索引值
        /// </summary>
        public int ItemIndex { get { return _ItemIndex == int.MaxValue ? 0 : _ItemIndex++; } }
        /// <summary>
        /// 机器名称
        /// </summary>
        public string Name
        {
            get; set;
        }
        /// <summary>
        /// IP地址
        /// </summary>
        public string IP
        {
            get; set;
        }
        /// <summary>
        /// 工作站编号
        /// </summary>
        public byte StationNo { get; set; }
        /// <summary>
        /// 采集器TCP Server端口号
        /// </summary>
        public int port { get; set; }
        /// <summary>
        /// 开模标识参数
        /// </summary>
        public string MoldOpendParam
        {
            get
            {
                if (string.IsNullOrEmpty(_MoldOpendParam))
                {
                    _MoldOpendParam = "SVs.Mold1.sv_bMoldOpen";
                };
                return _MoldOpendParam;
            }
            set { _MoldOpendParam = value; }
        }
 
        /// <summary>
        /// 计数器
        /// </summary>
        public int ProductCounter { get { _ProductCounter++; return _ProductCounter; } }
        #endregion
        /// <summary>
        /// 异步读事务ID
        /// </summary>
        public int AsyncReadTransactionId { get; set; }
        /// <summary>
        /// 异步报警事务ID
        /// </summary>
        public int AsyncAlarmTransId { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="Name">机器名</param>
        /// <param name="IP">机器IP</param>
        /// <param name="SrvPort">端口</param>
        /// <param name="StationNo">工作站编号</param>
        public MirleMachine(string Name, string IP, int SrvPort, byte StationNo)
        {
            this.Name = Name;
            this.IP = IP;
            this.port = SrvPort;
            this.StationNo = StationNo;
        }

        /// <summary>
        /// 启动
        /// </summary>
        public void StartMonitor()
        {
            //在离线定时器
            MyTimer = new Timer();
            MyTimer.Interval = 5000;
            MyTimer.Enabled = true;
            MyTimer.Elapsed += TimerElapsed;
            ConnServer(IP, port);
        }

        /// <summary>
        /// 启动
        /// </summary>
        public void Stop()
        {
            //在离线定时器
            this.MyTimer.Enabled = false;
            MyTimer = null;
            tcpClient.Close();
        }

        /// <summary>
        /// 定时器执行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            // if (_online) { return; }
            //_online = true;

            if (!tcpClient.Connected && tcpClient.Available==0) {
                //MachineStatusChange?.Invoke(this.Name, this.IP, false);
                _online = false;
            }
            MyTimer.Enabled = false;
            try
            {
                ConnServer(this.IP, this.port);
            }
            finally {
                MyTimer.Enabled = true;
            }
           
           // ReadData();
        }
        /// <summary>
        /// 质量参数属性
        /// </summary>
        public IList<string> QualityParams
        {
            get
            {
                if (_QualityParams == null)
                {
                    _QualityParams = new List<string>();
                }
                return _QualityParams;
            }
            set { _QualityParams = value; }
        }


        public event AlarmMsgDelegate AlarmEvent;
        public event ErrorMsgDelegate ErrorMsgEvent;
        public event EveryItemDelegate EveryItemEvent;
        public event MachineStatus MachineStatusChange;
        public event MoldClosed MoldClosedEvent;
        public event MoldOpen MoldOpenEvent;
        public event OpereationMoldDelegate OperationMoldEvent;
        public event ProductNumsDelegate ProductNumsEvent;
        /// <summary>
        /// 机器心跳事件
        /// </summary>
        public event MachineHeartbeat MachineHeartbeatEvent;

        /// <summary>
        /// 客户端
        /// </summary>
        private Socket tcpClient
        {
            get
            {
                if (_tcpClient == null || !_tcpClient.Connected)
                {
                    _tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                }
                return _tcpClient;
            }
        }
        /// <summary>
        /// 解析包
        /// </summary>
        public string PackageConfig { get; set; }

        /// <summary>
        /// 参数列表
        /// </summary>
        public IList<IVarItem> Items
        {
            get; set;
        }
        /// <summary>
        /// 心跳包发送频率(秒/次)
        /// </summary>
        public int HeartbeatRate
        {
            get; set;
        }

        public string CollectorId
        {
            get;set;
        }

        /// <summary>
        /// 开始连接服务端 
        /// </summary>
        /// <param name="srvIP"></param>
        /// <param name="srvPort"></param>
        private void ConnServer(string srvIP, int srvPort)
        {
            if (!_online) {
                using (SocketAsyncEventArgs SAEA = new SocketAsyncEventArgs())
                {
                    SAEA.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(srvIP), srvPort);
                    SAEA.Completed += ConnServerComplete;
                    SAEA.AcceptSocket = tcpClient;
                    SAEA.AcceptSocket.Blocking = false;
                    tcpClient.ConnectAsync(SAEA);
                }
            }            
        }

        /// <summary>
        /// 连接服务端动作完成
        /// </summary>
        /// <typeparam name="TEventArgs"></typeparam>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ConnServerComplete<TEventArgs>(object sender, TEventArgs e)
        {
            if (e == null)
            {
                MachineStatusChange?.Invoke(this.Name, this.IP, false);
                _online = false;
                return;
            }
            using (SocketAsyncEventArgs SAEA = (e as SocketAsyncEventArgs))
            {
                bool success = SAEA.SocketError == SocketError.Success;
                MachineStatusChange?.Invoke(this.Name, this.IP, success);
                if (success)
                {
                    _online = true;
                    ReadData();
                    ReceiveData();

                }
            }
        }

        /// <summary>
        /// 开始数据接收
        /// </summary>
        private void ReceiveData()
        {
            //_online = (tcpClient.Available > 0 && !tcpClient.Poll(1000, SelectMode.SelectRead));
            if (_Stop) { return; }
            using (SocketAsyncEventArgs SAEA = new SocketAsyncEventArgs())
            {
                SAEA.AcceptSocket = tcpClient;
                SAEA.SetBuffer(new byte[255], 0, 255);
                SAEA.Completed += ReceiveDataComplete;
                tcpClient.ReceiveAsync(SAEA);
            }
        }

        /// <summary>
        /// 数据接收完成
        /// </summary>
        /// <typeparam name="TEventArgs"></typeparam>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReceiveDataComplete<TEventArgs>(object sender, TEventArgs e)
        {
            using (SocketAsyncEventArgs SAEA = (e as SocketAsyncEventArgs))
            {
                MachineStatusChange?.Invoke(this.Name, this.IP, true);                   
                SAEA.AcceptSocket = tcpClient;
                int len = SAEA.BytesTransferred;
                byte[] buffer = SAEA.Buffer;
                if (len > 0) {
                    //是包的开始标识
                    byte[] ProcessBuffer = new byte[len];
                    Array.Copy(buffer, ProcessBuffer, len);
                    ErrorMsgEvent?.Invoke(this.Name, string.Format("Receive Buffer:{0}", BitConverter.ToString(ProcessBuffer).Replace("-", " ")), ErrorLevel.Info);
                    //解析数据
                    resolvePackage(ProcessBuffer);
                    resolvePackageAfter();
                }
                System.Threading.Thread.Sleep(1000);
                ReadData();
                ReceiveData();
            }
        }

        

        /// <summary>
        /// 发送读取数据的命令
        /// </summary>
        private void ReadData()
        {
            if (_Stop) { return; }
            try
            {
                int[] PLCAddrs = Items.Select(a => int.Parse(a.ItemVarName)).ToArray();
                byte[] readDataCmd = getReadPlcDataCmd(PLCAddrs);
                tcpClient.Send(readDataCmd);
                ErrorMsgEvent?.Invoke(this.Name,string.Format("Command Total:{0}\r\nCommand Content:{1} ", PLCAddrs.Length.ToString(), BitConverter.ToString(readDataCmd, 0, readDataCmd.Length).Replace("-", " ")),ErrorLevel.Info);
            }
            catch (Exception ex)
            {
                ErrorMsgEvent?.Invoke(this.Name, string.Format("ReadData Error:{0}\r\n{1}",ex.Message,ex.StackTrace),ErrorLevel.Error);
            }           
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="PLCAddrs"></param>
        /// <returns></returns>
        private byte[] getReadPlcDataCmd(int[] PLCAddrs)
        {
            try
            {
                IList<string> HL01 = new List<string>();
                string[] crcstr = MLHexHelper.CreateSendData(StationNo, 3, PLCAddrs);
                string HL = MLHexHelper.HighLowConvert(crcstr, HL01);
                string CRCStr = MLHexHelper.CRC16Modbus(HL);
                string sendCMD = string.Format("4641 4D41 {0} {1}", string.Join(" ", HL01), CRCStr);
                return MLHexHelper.HexStrToToHexByte(sendCMD);
            }
            catch (Exception ex)
            {
                ErrorMsgEvent?.Invoke(this.Name,string.Format("getReadPlcDataCmd Error:{0}\r\n{1}" ,ex.Message, ex.StackTrace),ErrorLevel.Error);
                return new byte[] { 0x00, 0x00 };
            }           
        }

        /// <summary>
        /// 解析数据
        /// </summary>
        /// <param name="ProcessBuffer"></param>
        private void resolvePackage(byte[] ProcessBuffer)
        {
            try
            {
                int len = Items.Count;
                int dataLen = len * 2;
                byte[] newData = ProcessBuffer.Skip(12).Take(len * 2).ToArray();
                ErrorMsgEvent?.Invoke(this.Name, string.Format("Recevce Data:{0}\n\n", BitConverter.ToString(newData).Replace("-"," ")), ErrorLevel.Info);
                DateTime CurrDT = DateTime.Now;
                for (int i = 0; i < len; i++)
                {
                    float newvalue = Convert.ToInt32(MLHexHelper.ByteToHexStr(new byte[] { newData[i * 2 + 1], newData[i * 2] }),16)* Items[i].Conversion;
                    
                    switch (Items[i].ItemCode)
                    {
                        case "A00001"://开模
                            _MoldOpening = newvalue == 1;
                            AddRunData(Items[i].ItemCode, newvalue.ToString(), CurrDT);
                            break;
                        case "A00002"://合模
                            _MoldCloseing = newvalue == 1;
                            AddRunData(Items[i].ItemCode, newvalue.ToString(), CurrDT);
                            break;
                        case "A00003"://操作模式
                            AddRunData(Items[i].ItemCode, (newvalue+1).ToString(), CurrDT);
                            if (!_LastKeyValues.ContainsKey(Items[i].ItemCode) || 
                                _LastKeyValues[Items[i].ItemCode]!= (newvalue + 1).ToString()) {
                                OperationMoldEvent?.Invoke(this.Name, this.IP, int.Parse((newvalue + 1).ToString()), null);
                            }
                            break;
                        case "A00007":
                            if (!RunDataList.Any(A => A.ItemName == Items[i].ItemCode && A.ItemValue == newvalue.ToString()))
                            {
                                EveryItemEvent?.Invoke(this.Name, this.IP, Items[i].ItemCode, newvalue.ToString(), CurrDT, _MoldCloseing);
                                AddRunData(Items[i].ItemCode, newvalue.ToString(), CurrDT);
                            }                            
                            break;
                        default://其他
                            AddRunData(Items[i].ItemCode, newvalue.ToString(), CurrDT);
                            if (_MoldOpening)
                            {
                                MoldOpenEvent?.Invoke(this.Name, this.IP, RunDataList);
                                _MoldOpening = false;
                            }
                            else {
                                if (_MoldCloseing)
                                {
                                    int js = ProductCounter;
                                    ProductNumsEvent?.Invoke(this.Name, this.IP, js, null);
                                    MoldClosedEvent?.Invoke(this, ProductCounter, OperationMode, RunDataList);
                                    RunDataList.Clear();
                                    _MoldCloseing = false;
                                }
                                else {
                                    if (!RunDataList.Any(A => A.ItemName == Items[i].ItemCode && A.ItemValue == newvalue.ToString()))
                                    {
                                        EveryItemEvent?.Invoke(this.Name, this.IP, Items[i].ItemCode, newvalue.ToString(), CurrDT, _MoldCloseing);
                                    }
                                }
                            }                            
                            break;
                    }
                    //记录上一笔数据
                    string ParaName = Items[i].ItemCode;
                    string ParaValue = newvalue.ToString();
                    if (_LastKeyValues.ContainsKey(ParaName))
                    {
                        if (_LastKeyValues[ParaName]!= ParaValue)
                        {
                            _LastKeyValues[ParaName] = ParaValue;
                        }
                    }
                    else
                    {
                        _LastKeyValues.Add(ParaName,ParaValue);
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorMsgEvent?.Invoke(this.Name, string.Format("resolvePackage Error:{0}\r\n{1}" ,ex.Message, ex.StackTrace),ErrorLevel.Error);
            }
        }

        /// <summary>
        /// 数据解析完成后
        /// </summary>
        private void resolvePackageAfter()
        {


        }

        /// <summary>
        /// 添加运行数据
        /// </summary>
        /// <param name="ParaName">参数名</param>
        /// <param name="ParaValue">参数值</param>
        /// <param name="GetTime">获取时间</param>
        private void AddRunData(string ParaName, string ParaValue, DateTime GetTime, List<RunData> rundata = null)
        {
            if (rundata != null)
            {
                rundata.Add(new RunData { ItemIndex =this.ItemIndex, ItemName = ParaName, ItemValue = ParaValue, Updatetime = GetTime, Createtime = DateTime.Now });
            }
            else
            {
                if (RunDataList.Any(A => A.ItemName == ParaName && A.ItemValue == ParaValue))
                {
                    RunDataList.Where(A => A.ItemName == ParaName && A.ItemValue == ParaValue).ToList().ForEach(A => { A.Createtime = GetTime; A.Updatetime = GetTime; });
                }
                else {
                    RunDataList.Add(new RunData { ItemIndex = this.ItemIndex, ItemName = ParaName, ItemValue = ParaValue, Updatetime = GetTime, Createtime = DateTime.Now });
                }
                
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            _Stop = true;
            //在离线定时器
            if (MyTimer != null) {
                this.MyTimer.Enabled = false;
            }
            MyTimer = null;
            if (_tcpClient != null && _tcpClient.Connected)
            {
                _tcpClient.Close();
            }
            _tcpClient = null;
        }
    }
}
